package com.dragon.mmochat.service;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dragon.mmochat.MmochatConstant;
import com.dragon.mmochat.MmochatMain;
import com.dragon.mmochat.model.enumType.MmochatBigMoneyCostType;
import com.dragon.mmochat.model.enumType.MmochatBigMoneyEarnType;
import com.dragon.mmochat.model.enumType.MmochatPropType;
import com.dragon.mmochat.model.enumType.MmochatSmallMoneyCostType;
import com.dragon.mmochat.model.enumType.MmochatSmallMoneyEarnType;
import com.dragon.mmochat.model.scheduleJob.DailyStatisticsJob;
import com.dragon.mmochat.model.scheduleJob.MonthlyStatisticsJob;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

//数据统计服务
public class MmochatStatisticsService {
	private static Logger log = LoggerFactory
			.getLogger(MmochatStatisticsService.class);

	public static ScheduledExecutorService onlineRecordThread = Executors
			.newScheduledThreadPool(1);

	public static Scheduler sched = null;

	// 当日新注册用户数
	public static int todayRegisterNum = 0;

	// 当日填写了推荐人的注册玩家数
	public static int todayRegisterWithSuggesterNum = 0;

	// 当日登陆用户数:roleId->0
	public static Map<Integer, Byte> todayLoginNum = new ConcurrentHashMap<Integer, Byte>();

	// 当日最高在线
	// 挂靠在MmochatMainService.CheckManLeaveGame中,每分钟一次
	public static int todayMaxOnlineNum = 0;

	// 当日时段在线:(new Date()).getTime()-->onlineNum
	// 每隔15分钟记录一次
	public static Map<Long, Integer> todayOnlineMap = new ConcurrentHashMap<Long, Integer>();

	// 当日在线总时长(当前在线的用户计算到下一天中,单位:ms)
	// 玩家离线时统计
	public static long todayTotalOnlineTime = 0;

	// 昨天注册今日再次登陆的用户数
	public static Map<Integer, Byte> todayLoginNumOfYesterdayRegMap = new ConcurrentHashMap<Integer, Byte>();

	// 当日直付人员roleId-->直付金额(单位：分)
	public static Map<Integer, Integer> todayDirectPayMap = new ConcurrentHashMap<Integer, Integer>();

	// 当日直付总次数
	public static int todayDirectPayTimes = 0;

	// 当日K币兑换人员roleId-->K币兑换额
	public static Map<Integer, Integer> todayKBPayMap = new ConcurrentHashMap<Integer, Integer>();

	// 当日K币兑换总次数
	public static int todayKBPayTimes = 0;

	// 当日元宝消费额
	public static int todayBigMoneyCost = 0;

	// 主要游戏币消耗点的当天消耗量
	public static Map<MmochatSmallMoneyCostType, Long> todaySmallMoneyCostPointMap = new ConcurrentHashMap<MmochatSmallMoneyCostType, Long>();

	// 主要游戏币产出点的当天产出量
	public static Map<MmochatSmallMoneyEarnType, Long> todaySmallMoneyEarnPointMap = new ConcurrentHashMap<MmochatSmallMoneyEarnType, Long>();

	// 主要元宝消耗点的当天消耗量
	public static Map<MmochatBigMoneyCostType, Long> todayBigMoneyCostPointMap = new ConcurrentHashMap<MmochatBigMoneyCostType, Long>();

	// 主要元宝产出点的当天产出量
	public static Map<MmochatBigMoneyEarnType, Long> todayBigMoneyEarnPointMap = new ConcurrentHashMap<MmochatBigMoneyEarnType, Long>();

	// 当日推荐积分兑换元宝量(元宝额)
	public static long todayScore2BigMoneyCount = 0;

	// 当日推荐积分兑换K币量(K币额)
	public static long todayScore2KBCount = 0;

	// 道具的当天消耗量
	public static Map<MmochatPropType, Long> todayPropCostPointMap = new ConcurrentHashMap<MmochatPropType, Long>();

	public void init() throws Exception {
		if (!MmochatMain.openMergeServer) {
			onlineRecordThread.scheduleWithFixedDelay(new OnlineRecord(), 1,
					15, TimeUnit.MINUTES);

			log.debug("init MmochatStatisticsService");
			SchedulerFactory sf = new StdSchedulerFactory();
			sched = sf.getScheduler();

			/***************** 每日数据统计(每日3:50触发) *****************/
			{
				// 通过JobDetail，将定时程序做为quartz job
				JobDetail job = new JobDetail("dailyStatistics",
						"dailyStatisticsGroup", DailyStatisticsJob.class);

				// 创建trigger,触发定时程序
				CronTrigger trigger = new CronTrigger();
				trigger.setName("dailyStatisticsTrigger");
				trigger.setCronExpression("0 50 3 * * ?");

				// 通过trigger 调度job
				sched.scheduleJob(job, trigger);
			}
			/***************** 每月数据统计(每月1号4:20触发) *****************/
			{
				// 通过JobDetail，将定时程序做为quartz job
				JobDetail job = new JobDetail("monthlyStatistics",
						"monthlyStatisticsGroup", MonthlyStatisticsJob.class);

				// 创建trigger,触发定时程序
				CronTrigger trigger = new CronTrigger();
				trigger.setName("monthlyStatisticsTrigger");
				trigger.setCronExpression("0 20 4 1 * ?");

				// 通过trigger 调度job
				sched.scheduleJob(job, trigger);
			}

			/* 所有定时任务已加载，启动 */
			sched.start();
		}
	}

	class OnlineRecord implements Runnable {
		@Override
		public void run() {
			try {
				// 记录当前时段在线人数
				MmochatStatisticsService.todayOnlineMap
						.put(System.currentTimeMillis(),
								MmochatMainService.players.size());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	// 保存每日数据到数据库
	// 0、累计总注册用户数:select count(id) from role
	// 1、当日新注册用户数:todayRegisterNum
	// 2、当日登陆用户数:todayLoginNum.size()
	// 3、当日最高在线:todayMaxOnlineNum
	// 4、当日各时间点的在线人数:todayOnlineMap
	// 5、当日平均在线时长:当日在线总时长(todayTotalOnlineTime)/当日登陆用户数
	// 6、当日直付人数-->直付额:todayDirectPayMap
	// 7、当日K币兑换玩家-->K币兑换额:dayKBPayMap
	// 8、元宝消费人数:todayBigMoneyRoleCostMap:key
	// 9、元宝消费总额:todayBigMoneyRoleCostMap:value
	// 10、元宝剩余总量:select sum(big_money) from role
	// 11、7天内登陆过的用户数:select count(id) from role where latest_login_time
	// >= now.getTime()-7dayTime
	// 12、游戏币剩余总量:select sum(small_money)+sum(stored_small_money) from role
	// 13、主要游戏币消耗点的当天消耗量:todaySmallMoneyCostPointMap
	// 14、主要游戏币产出点的当天产出量:todaySmallMoneyEarnPointMap
	// 15、主要元宝消耗点的当天消耗量:todayBigMoneyCostPointMap
	// 16、主要元宝产出点的当天产出量:todayBigMoneyEarnPointMap
	public static void saveDailyStatisticsToSql() {
		System.out.println("开始保存每日统计数据到数据库");
		long t = System.currentTimeMillis();

		Gson gson = new Gson();
		long timeBefore7Day = new Date().getTime() - MmochatConstant.MS_PER_DAY
				* 7;
		long timeBefore6DayTick = new Date().getTime()
				- MmochatConstant.MS_PER_DAY * 6;
		Timestamp timeBefore6Day = new Timestamp(timeBefore6DayTick);
		int reg_num = todayRegisterNum;
		int login_num = todayLoginNum.size();
		int loginNumOfYesterdayReg = todayLoginNumOfYesterdayRegMap.size();
		int online_top = todayMaxOnlineNum;
		int online_time_average = (int) (todayTotalOnlineTime
				/ Math.max(1, login_num) / MmochatConstant.MS_PER_MINUTE);
		int bigmoney_pay_role_num = 0;
		int bigmoney_pay_total_money = todayBigMoneyCost;

		String main_point_to_cost_smallmoney = gson.toJson(
				todaySmallMoneyCostPointMap,
				new TypeToken<Map<MmochatSmallMoneyCostType, Long>>() {
				}.getType());
		String main_point_to_produce_smallmoney = gson.toJson(
				todaySmallMoneyEarnPointMap,
				new TypeToken<Map<MmochatSmallMoneyEarnType, Long>>() {
				}.getType());
		String main_point_to_cost_bigmoney = gson.toJson(
				todayBigMoneyCostPointMap,
				new TypeToken<Map<MmochatBigMoneyCostType, Long>>() {
				}.getType());
		String main_point_to_produce_bigmoney = gson.toJson(
				todayBigMoneyEarnPointMap,
				new TypeToken<Map<MmochatBigMoneyEarnType, Long>>() {
				}.getType());
		String online_map = gson.toJson(todayOnlineMap,
				new TypeToken<Map<Long, Integer>>() {
				}.getType());
		String direct_pay_role_money_map = gson.toJson(todayDirectPayMap,
				new TypeToken<Map<Integer, Integer>>() {
				}.getType());
		String kb_pay_role_money_map = gson.toJson(todayKBPayMap,
				new TypeToken<Map<Integer, Integer>>() {
				}.getType());
		String today_propCostPointMap = gson.toJson(todayPropCostPointMap,
				new TypeToken<Map<MmochatPropType, Long>>() {
				}.getType());

		try {
			MmochatDao.saveDailyStatistics(timeBefore7Day, timeBefore6Day,
					reg_num, login_num, loginNumOfYesterdayReg, online_top,
					online_time_average, bigmoney_pay_role_num,
					bigmoney_pay_total_money, main_point_to_cost_smallmoney,
					main_point_to_produce_smallmoney,
					main_point_to_cost_bigmoney,
					main_point_to_produce_bigmoney, online_map,
					direct_pay_role_money_map, kb_pay_role_money_map,
					todayRegisterWithSuggesterNum, todayScore2BigMoneyCount,
					todayScore2KBCount, todayDirectPayTimes, todayKBPayTimes, today_propCostPointMap);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 数据重置
		todayRegisterNum = 0;
		todayLoginNum.clear();
		todayLoginNumOfYesterdayRegMap.clear();
		todayMaxOnlineNum = 0;
		todayTotalOnlineTime = 0;
		todayBigMoneyCost = 0;
		todaySmallMoneyCostPointMap.clear();
		todaySmallMoneyEarnPointMap.clear();
		todayBigMoneyCostPointMap.clear();
		todayBigMoneyEarnPointMap.clear();
		todayOnlineMap.clear();
		todayDirectPayMap.clear();
		todayKBPayMap.clear();
		todayDirectPayTimes = 0;
		todayKBPayTimes = 0;
		todayPropCostPointMap.clear();

		t = System.currentTimeMillis() - t;
		System.out.println("保存每日统计数据完成，耗时:" + t + "ms");
	}

	// 保存每月数据到数据库
	// 1、活跃用户数:select count(id) from role where cur_month_login_day_num >= 2
	// 2、当月总登陆用户数:select count(id) from role where latest_login_time >=
	// lastMonthTime
	// 3、当月总付费用户数:select count(id) from role where latest_pay_time >=
	// lastMonthTime
	// 4、当月总付费额(直付、K币)
	// select sum(rmb) from direct_pay_orders where create_time >= lastMonthTime
	// select sum(kb) from kb_pay_orders where create_time >= lastMonthTime
	public static void saveMonthlyStatisticsToSql() {
		System.out.println("开始保存每月统计数据到数据库");
		long t = System.currentTimeMillis();

		try {
			MmochatDao.saveMonthlyStatistics();
		} catch (Exception e) {
			e.printStackTrace();
		}

		t = System.currentTimeMillis() - t;
		System.out.println("保存每月统计数据完成，耗时:" + t + "ms");
	}

	// v为负数
	public static void addSmallMoneyCost(MmochatSmallMoneyCostType costType,
			int v) {
		if (costType == null || v == 0) {
			return;
		}
		long add = (long) v;
		Long count = todaySmallMoneyCostPointMap.get(costType);
		if (count == null) {
			count = add;
		} else {
			count += add;
		}
		todaySmallMoneyCostPointMap.put(costType, count);
	}

	public static void addSmallMoneyEarn(MmochatSmallMoneyEarnType earnType,
			int v) {
		if (earnType == null || v == 0) {
			return;
		}
		long add = (long) v;
		Long count = todaySmallMoneyEarnPointMap.get(earnType);
		if (count == null) {
			count = add;
		} else {
			count += add;
		}
		todaySmallMoneyEarnPointMap.put(earnType, count);
	}

	public static void addBigMoneyCost(MmochatBigMoneyCostType costType, int v) {
		if (costType == null || v == 0) {
			return;
		}
		long add = (long) v;
		Long count = todayBigMoneyCostPointMap.get(costType);
		if (count == null) {
			count = add;
		} else {
			count += add;
		}
		todayBigMoneyCostPointMap.put(costType, count);
	}

	public static void addBigMoneyEarn(MmochatBigMoneyEarnType earnType, int v) {
		if (earnType == null || v == 0) {
			return;
		}
		long add = (long) v;
		Long count = todayBigMoneyEarnPointMap.get(earnType);
		if (count == null) {
			count = add;
		} else {
			count += add;
		}
		todayBigMoneyEarnPointMap.put(earnType, count);
	}

	public static void addPropCost(MmochatPropType propType, int v) {
		if (propType == null || v == 0) {
			return;
		}
		long add = (long) v;
		Long count = todayPropCostPointMap.get(propType);
		if (count == null) {
			count = add;
		} else {
			count += add;
		}
		todayPropCostPointMap.put(propType, count);
	}
}
